#ifndef esda_UnaryOperatorStream_h
#define esda_UnaryOperatorStream_h

/*
 * Copyright (C) Olivier Le Floch
 *
 * See the file LICENSE for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "common.h"
#include "Timestamp.h"
#include "Streams.h"

namespace esda
{
	class UnaryOperatorStream : public OperatorStream<double> {
	protected:
		Stream<double> &inputStream;
		
	public:
		UnaryOperatorStream(Stream<double> &inStream) : inputStream(inStream) {};
		
		virtual Timestamp getNextStart(const Timestamp t) const;
		virtual Timestamp getNextEnd(const Timestamp t) const;
		virtual Timestamp getPreviousStart(const Timestamp t) const;
		virtual Timestamp getPreviousEnd(const Timestamp t) const;
		
		virtual Timestamp getFirstStart() const;
		virtual Timestamp getLastEnd() const;
		
		virtual double get(const Timestamp t) const;
	};
	
	class UnaryOperatorStream_Scale : public UnaryOperatorStream {
		const double scale;
		
	public:
		UnaryOperatorStream_Scale(Stream<double> &inStream, double sc) : UnaryOperatorStream(inStream), scale(sc) {};
		
		double get(const Timestamp t) const;
	};
	
	class UnaryOperatorStream_Shift : public UnaryOperatorStream {
		const double shift;
		
	public:
		UnaryOperatorStream_Shift(Stream<double> &inStream, double sh) : UnaryOperatorStream(inStream), shift(sh) {};
		
		double get(const Timestamp t) const;
	};
	
	class UnaryOperatorStream_Timeshift : public UnaryOperatorStream {
		const Timestamp::inttype timeshift;
		
	public:
		UnaryOperatorStream_Timeshift(Stream<double> &inStream, long long int timesh) : UnaryOperatorStream(inStream), timeshift(timesh) {};
		
		virtual double get(const Timestamp t) const;
		
		virtual Timestamp getNextStart(const Timestamp t) const;
		virtual Timestamp getNextEnd(const Timestamp t) const;
		virtual Timestamp getPreviousStart(const Timestamp t) const;
		virtual Timestamp getPreviousEnd(const Timestamp t) const;
		
		virtual Timestamp getFirstStart() const;
		virtual Timestamp getLastEnd() const;
	};
	
	class UnaryOperatorStream_UserFunc : public UnaryOperatorStream {
	public:
		typedef double (*UserFunc)(double);
		
		UnaryOperatorStream_UserFunc(Stream<double> &inStream, UserFunc userF) : UnaryOperatorStream(inStream), userFunc(userF) {};
		
		double get(const Timestamp t) const;
	
	private:
		UserFunc userFunc;
	};
	
	double UnaryOperatorStream::get(const Timestamp t) const {
		return inputStream.get(t);
	}
	
	Timestamp UnaryOperatorStream::getNextStart(const Timestamp t) const {
		return inputStream.getNextStart(t);
	}
	
	Timestamp UnaryOperatorStream::getNextEnd(const Timestamp t) const {
		return inputStream.getNextEnd(t);
	}
	
	Timestamp UnaryOperatorStream::getPreviousStart(const Timestamp t) const {
		return inputStream.getPreviousStart(t);
	}

	Timestamp UnaryOperatorStream::getPreviousEnd(const Timestamp t) const {
		return inputStream.getPreviousEnd(t);
	}

	Timestamp UnaryOperatorStream::getFirstStart() const {
		return inputStream.getFirstStart();
	}
	
	Timestamp UnaryOperatorStream::getLastEnd() const {
		return inputStream.getLastEnd();
	}
	
	// Mise à l'échelle spatiale
	double UnaryOperatorStream_Scale::get(const Timestamp t) const {
		return inputStream.get(t) * scale;
	}
	
	// Décallage spatial
	double UnaryOperatorStream_Shift::get(const Timestamp t) const {
		return inputStream.get(t) + shift;
	}
	
	// Décallage temporel
	double UnaryOperatorStream_Timeshift::get(const Timestamp t) const {
		return inputStream.get(t + timeshift);
	}
	
	Timestamp UnaryOperatorStream_Timeshift::getNextStart(const Timestamp t) const {
		return inputStream.getNextStart(t) + timeshift;
	}
	
	Timestamp UnaryOperatorStream_Timeshift::getNextEnd(const Timestamp t) const {
		return inputStream.getNextEnd(t) + timeshift;
	}
	
	Timestamp UnaryOperatorStream_Timeshift::getPreviousStart(const Timestamp t) const {
		return inputStream.getPreviousStart(t) + timeshift;
	}

	Timestamp UnaryOperatorStream_Timeshift::getPreviousEnd(const Timestamp t) const {
		return inputStream.getPreviousEnd(t) + timeshift;
	}

	Timestamp UnaryOperatorStream_Timeshift::getFirstStart() const {
		return inputStream.getFirstStart() + timeshift;
	}
	
	Timestamp UnaryOperatorStream_Timeshift::getLastEnd() const {
		return inputStream.getLastEnd() + timeshift;
	}
	
	double UnaryOperatorStream_UserFunc::get(const Timestamp t) const {
		return userFunc(inputStream.get(t));
	}
}

#endif
